package com.bigbigrain.jiagou.controller;

import com.bigbigrain.jiagou.domain.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @description: 缓存controller  缓存代码注解要加到service层 这里只是方便测试
 * @author: B1gB1gRA1n
 * @create: 2020-4-22 11:53
 **/
@RestController
@RequestMapping("/cache")
public class CacheController {
    /**
     * @Cacheable ⽤来声明⽅法是可缓存的，将结果存储到缓存中以便后续使⽤相同参数调⽤时不需执⾏实际的
     * ⽅法，直接从缓存中取值。 @Cacheable 可以标记在⼀个⽅法上，也可以标记在⼀个类上。当标记在⼀个⽅
     * 法上时表示该⽅法是⽀持缓存的，当标记在⼀个类上时则表示该类所有的⽅法都是⽀持缓存的。
     *
     * @Cacheable(value="helloCache") 这个注释的意思是，当调⽤这个⽅法时，会从⼀个名叫 helloCache 的缓
     *      存中查询，如果没有，则执⾏实际的⽅法（也可是查询数据库），并将执⾏的结果存⼊缓存中，否则返回缓
     *      存中的对象。这⾥的缓存中的 key 就是参数 name， value 就是返回的 String 值。
     * @Cacheable ⽀持如下⼏个参数。
     *      value：缓存的名称。
     *      key：缓存的 key，可以为空，如果指定要按照 SpEL 表达式编写；如果不指定，则缺省按照⽅法的所有
     *      参数进⾏组合。
     *      condition：触发条件，只有满⾜条件的情况才会加⼊缓存，默认为空，既表示全部都加⼊缓存，⽀持
     *      SpEL。
     *
     *   重点补充：需要注意的是当⼀个⽀持缓存的⽅法在对象内部被调⽤时是不会触发缓存功能的。--代理 切面原因
     */
    @RequestMapping("/hello")
    @Cacheable(value="helloCache")
    public String hello(String name) {
        System.out.println("没有走缓存！");
        return "hello "+name;
    }

    @RequestMapping("/condition")
    @Cacheable(value="condition",condition="#name.length() <= 4")
    public String condition(String name) {
        System.out.println("没有走缓存！");
        return "hello "+name;
    }

    @RequestMapping("/getUsers")
    @Cacheable(value="usersCache",key="#nickname",condition="#nickname.length() >= 6")
    public List<User> getUsers(String nickname) {
        List<User> list = new ArrayList<>();
        list.add(new User("zhangy1", "zhangy1", "youknow1", "know1","2020"));
        list.add(new User("zhangy2", "zhangy2", "youknow2", "know2","2020"));
        list.add(new User("zhangy2", "zhangy3", "youknow3", "know3","2020"));
        System.out.println("执行了数据库操作");
        return list;
    }

    /**
     * @CachePut 标注的⽅法在执⾏前，不会去检查缓存中是否存在之前执⾏过的结果，⽽是每次都会执⾏该⽅法，并将执⾏结果以键值对的形式存⼊指定的缓存中。
     *@CachePut 配置⽅法
     *      value 缓存的名称。
     *      key 缓存的 key，可以为空，如果指定要按照 SpEL 表达式编写，如果不指定，则缺省按照⽅法的所有参数进⾏组合。
     *      condition 缓存的条件，可以为空，使⽤ SpEL 编写，返回 true 或者 false，只有为 true 才进⾏缓存
     */
    @RequestMapping("/getPutUsers")
    @CachePut(value="usersCache",key="#nickname")
    public List<User> getPutUsers(String nickname) {
        List<User> list = new ArrayList<>();
        list.add(new User("zhangy1", "zhangy1", "youknow1", "know1","2020"));
        list.add(new User("zhangy2", "zhangy2", "youknow2", "know2","2020"));
        list.add(new User("zhangy2", "zhangy3", "youknow3", "know3","2020"));
        System.out.println("执行了数据库操作");
        return list;
    }

    /**
     * @CacheEvict 是⽤来标注在需要清除缓存元素的⽅法或类上的，当标记在⼀个类上时表示其中所有的⽅法的
     *      执⾏都会触发缓存的清除操作。
     * @CacheEvict 可以指定的属性有 value、 key、 condition、 allEntries 和beforeInvocation，其中 value、 key 和 condition 的语义与 @Cacheable 对应的属性类似。
     *      即 value 表示清除操作是发⽣在哪些 Cache 上的（对应 Cache 的名称）；
     *      key 表示需要清除的是哪个 key，如未指定则会使⽤默认策略⽣成的 key；
     *      condition 表示清除操作发⽣的条件。
     *      allEntries 属性
     *          allEntries 是 boolean 类型，表示是否需要清除缓存中的所有元素，默认为 false，表示不需要。当指定了
     *          allEntries 为 true 时， Spring Cache 将忽略指定的 key，有的时候我们需要 Cache ⼀下清除所有的元素，这
     *          ⽐⼀个⼀个清除元素更有效率。
     *          在上⼀个⽅法中我们使⽤注解： @CachePut(value="usersCache",key="#nickname") 来更新缓
     *          存，但如果不写 key="#nickname" ， Spring Boot 会以默认的 key 值去更新缓存，导致最上⾯的
     *          getUsers() ⽅法并没有获取最新的数据。但是现在我们使⽤ @CacheEvict 就可以解决这个问题了，它会将所
     *          有以 usersCache 为名的缓存全部清除
     *      beforeInvocation 属性
     *          清除操作默认是在对应⽅法成功执⾏之后触发的，即⽅法如果因为抛出异常⽽未能成功返回时也不会触发清
     *          除操作。使⽤ beforeInvocation 可以改变触发清除操作的时间，当我们指定该属性值为 true 时， Spring 会在
     *          调⽤该⽅法之前清除缓存中的指定元素.
     *
     */
    @RequestMapping("/allEntries")
    @CacheEvict(value="usersCache", allEntries=true)
    public String  allEntries(String nickname) {
        String msg="执行了allEntries";
        System.out.println(msg);
        return msg;
    }

    @RequestMapping("/beforeInvocation")
    @CacheEvict(value="usersCache", allEntries=true, beforeInvocation=true)
    public void beforeInvocation() {
        throw new RuntimeException("test beforeInvocation");
    }
}
